

// 1. convert转化坐标的时候, 比如 xxx.convert(frame, to: view), 这个frame是相对于xxx的frame, 换而言之, 要用这个frame对应控件本身的superview作为xxx, 而不是爷爷辈

// 2. 控件通过CGAffine旋转的时候  是会改变frame的, 如果在旋转的过程中重新设置他的frame 就会导致奇怪现象, 所以如果想要同时做旋转和位移动画, 应该搞两个view, superView做位移动画, 里面子内容做旋转动画

// 3. 控制器addChild, 添加子控制器, 如果已经添加过, 会将上一次的remove掉, 再次添加, 总的长度不影响, 还是一个

// 4. 判断两个rect是否交叉intersects

/**
 * ceil(x)返回不小于x的最小整数值 (然后转换为double型)       2.5 -> 3
 * floor(x)返回不大于x的最大整数值                         2.5 -> 2
 * round(x)返回x的四舍五入整数值                          2.5 -> 3, 2.4 -> 2
 */


// 5. 如果先添加subView, 后添加addSublayer, (layer画线之类), 那么layer的画线会穿透子视图, 即layer是放在view的最上层的, 如果不想layer穿透子视图, 要先添加layer

// 6. shapeLayer配合gradientLayer使用的时候, 如果是想画渐变曲线, shapeLayer的lineColor一定要设置成非透明, fillColor一定要设置成透明 参考CircleChartView

// 7. 给Aview设置tranform, 会导致控制器调用layoutSubviews方法, 如果在里面设置了Aview的frame, 会导致Aview在旋转的过程中变形. 解决方法, Aview添加一个flag, 在pan手势开始设为true, 结束设为end, 在控制器的layout中判断是在手势旋转中, 不给这个view设置frame, 即可解决旋转过程中变形的问题, 参考CircleChartView

// 8. 通过pan手势旋转控件, 利用pan的translationPoint, 计算其长度, 和pan手势的startPoint距view中心点距离作为半径, 可以计算出单次移动的圆心角, 然后对其transform设置即可, 参考CircleChartView

// 9. sin, cos, tan三角函数的反函数: asin, acos, atan (全称为arcsin, arccos, arctan)

// 10. 添加shapeLayer动画要在init中, 如果写到layoutSubviews中会导致其添加多次, 如果想要监听动画的stop状态会变得很乱, 第二次添加动画, 移除第一次添加动画的时候, 会导致第一次动画的stop代理执行, 导致判断动画执行完成时机错误, 参考CircleChartView

// 11. CAShapeLayer画圆,  可以在layout中设置path, 如果需要做动画, 写在init里, 不影响动画执行. 会按照layout中的设置path执行动画.  封装一个动画控件可以给一个animateTime, 如果这个值不设置, 或者为0, 则没有动画, 如果这个值设置了一个正数, 就有动画, 动画时间就是animateTime

// 12. 如果贝塞尔曲线要stroke或者fill, 一定要写在drawRect方法中, 不然找不到上下文, 但是如果只是单纯的设置贝塞尔曲线的形状, 线条之类的, 可以写在layoutSubviews里面

// 13. awakefromnib不能点属性的原因, override class func awakeFromNib 系统有时候会生成带有class的类方法, 导致问题... 删除class即可

// 14.  let point = controller.mapView.convert(model.locationModel.coordinate, toPointTo: controller.mapView)
//      controller.mapView.setZoomLevel(19, atPivot: point, animated: true)
//      基于atPivot点进行zoom变化

// 15. mapview的selectAnnotations是不能被remove掉的, 所以在聚合处理的时候很恶心人, 只能通过index

// 16. headerBar & bottomBar屏蔽其父view(self)的tap事件 如果不屏蔽现象: bottomBar的按钮, 快速点击的时候响应比较慢, 和双击手势冲突了(双击手势设置优先于单击)

// 17. view的superview只能有一个, 所以NetWorkErrorView可以自由在window和view之间切换, 即便之前添加在了window上,但是一旦view调用, 会自动从window上移除添加到view上, 不需要我们手动操作

// 18. collectionViewLayout:sizeForItemAt不调用, public extension xxx: UICollectionViewDelegate, UICollectionViewDelegateFlowLayout, 没有引用UICollectionViewDelegateFlowLayout

// 19. 监听系统自带的navigationBar返回, 或者监听系统自带的侧滑返回方法如下:
/**
 
 navigationController?.interactivePopGestureRecognizer?.addTarget(self, action: #selector(action))
 
 @objc func action(sender: UIScreenEdgePanGestureRecognizer) {
     if sender.state == .ended {
         print("返回执行了.....")
     }
 }
 */

// 20. 正则表达式, 包含A 且不包含B 且不包含C  ^((?!(B)|(C)).)*A

/** 21. swift和oc混编的小知识:
     
 ① OC项目使用swift混编的时候, 如果oc项目里有pch要注意, pch如果引用了-swift.h文件, -swift.h又import了pch, 那么就会导致循环import导致一直报错, 但是报错内容还不好检查出来是此类原因, 尤为注意.
      
 ② OC类想要访问swift类, 需要导入 target名-Swift.h, 如果是多个target的项目, 可能产生多个target名-Swift.h文件, 那么假设A类的OC文件同时被TargetA和TargetB引用, 且想要引用swift.h头文件, 还需要判断是当前是哪个target, 否则假设编译target选了 TargetA, 那么不能import "TargetB-Swift.h", 一个好的解决办法是, 首先, 在target的build settings里面搜索preprocessor macros, 给不同的target添加同一个key, 如给TargetA添加ZB_Environment=1, 给TargetB添加ZB_Environment=2, 那么可以创建一个ZBSwiftHeader.h头文件, 里面写入代码:
 
 #ifndef ZBSwiftHeader_h
 #define ZBSwiftHeader_h


 #ifdef ZB_Environment

 #if ZB_Environment == 1

 #import "MTZJ-Swift.h"

 #else

 #import "MTZJ_Test-Swift.h"

 #endif
 #endif

 #endif /* ZBSwiftHeader_h */
 
 
 然后在需要使用swift文件的oc文件中导入该文件即可自动判断当前是哪个target, 应该导入哪个.
 
 
 ③  swift想要使用oc的文件, 需要手动创建 target名-Briding-Header.h文件, 一般在首次在oc工程创建swift, 或者首次在swift创建oc文件时, xcode会自动为我们创建, 如果没有, 需要我们手动创建, 如果是手动创建, 命名可以不按照-Briding-Header.h格式命名, 创建完文件之后, 在build settings里面搜Briding, 设置path, 在XXX-Briding-Header.h中导入的文件, 皆可在swift中使用
 
 
 
 */



// todofix:
